#ifndef typeinter_compil_h
#define typeinter_compil_h

#include <iostream>
#include <vector>
#include <cstdlib>
#include "Type.hpp"
#include "TypeBase.hpp"
#include "Value.hpp"
#include "IdentTable.hpp"
#include "SymbolTable.hpp"


class TypeInter: public Type {
	private:
		int minLimit;
		int maxLimit;
		OrderedType * typeLim;

	public:
		TypeInter(){
			minLimit=0;
			maxLimit =0;
			typeLim = NULL;
		}

		TypeInter(int minLimit, int maxLimit, OrderedType * typeElem){
			this->minLimit = minLimit;
			this->maxLimit = maxLimit;
			this->typeLim = typeElem;
		}

		~TypeInter() {
			if(typeLim != NULL ) {
				delete typeLim;
			}

		}
		void show(){
			std::cout << "<";
			typeLim->show();
			std::cout << "," <<  minLimit << "," << maxLimit << ">" ;
		}

		bool belongs(Value * val, OrderedType * type){
			if( ! val->isOfType( this->typeLim )) {
				return false;
			}

			int ord = type->getOrd(val);
			if(ord < minLimit || ord > maxLimit){
				return false;
			}
			return true;
		}

		/*
		 *

		virtual bool operator==(const TypeInter &toCompare ) {
			if(this->minLimit != toCompare.minLimit){
				return false;
			}
			if(this->maxLimit != toCompare.minLimit){
				return false;
			}
			if( (*this->typeLim) != (*toCompare.typeLim) ) {
				return false;
			}

			return true;
		}

		virtual bool operator!=(const TypeInter &toCompare ) {
			return !( *this == toCompare );
		}

		*/
		virtual Type * clone() {
			TypeInter * t = new TypeInter(minLimit, maxLimit, (OrderedType *) typeLim->clone() );
			return t;
		}

};


class TypeArrayIndexList: public Type {
	private:
		std::vector<TypeInter*> * indexList; 

	public:
		TypeArrayIndexList(){
			this->indexList = new std::vector<TypeInter *>();
		}

		~TypeArrayIndexList(){
			for(unsigned int i=0;i<this->indexList->size(); i++) {
				delete (*this->indexList)[i];
			}
			delete this->indexList;
		}

		void show(){
			for(unsigned int i=0;i<this->indexList->size();i++){
					(*indexList)[i]->show();
			//	if(i!=this->indexList->size()-1){
			//		std::cout << ",";
			//	}
			}
		}

		void addInter(TypeInter * interval) {
			this->indexList->push_back(interval);
		}

		/*
		 *
		virtual bool operator==(const Type &toCompare ) {
			if (this->theType != toCompare.theType )  {
				return false;
			}
			return *this == (TypeArrayIndexList &) toCompare ;
		}
		*/
		virtual bool operator==(const TypeArrayIndexList &toCompare ) {
			for(unsigned int i=0; i<this->indexList->size(); i++) {
				if( *(*this->indexList)[i] != *(*toCompare.indexList)[i] ){
					return false;
				}
			}
			return true;
		}

		virtual bool operator!=(const TypeArrayIndexList &toCompare ) {
			return !( *this == toCompare );
		}

		Type * clone() {
			TypeArrayIndexList * t = new TypeArrayIndexList();
			for(unsigned int i=0;i<this->indexList->size(); i++) {
				t->addInter( (TypeInter *) (*this->indexList)[i]->clone() );
			}
			return t;
		}

};

class TypeArray: public Type {
	private:
		Type * theTypeOfElem;
		TypeArrayIndexList * theIndexList;

	public:
		TypeArray(Type * typeOfElem, TypeArrayIndexList *indexList) {
			this->theType = TYPE_ARRAY;
			this->theTypeOfElem = typeOfElem;
			this->theIndexList = indexList;
		}

		~TypeArray() {
			delete this->theTypeOfElem;
			delete this->theIndexList;
		}

		void show(){
			std::cout << "[";
			theIndexList->show();
			std::cout << ",";
			theTypeOfElem->show();
			std::cout << "]";
		}

		bool equals(const Type &toCompare){
			if(this->theType != toCompare.theType){
				return false;
			} else {
				return this->equals((TypeArray&) toCompare);
			}
		}

		bool equals(const TypeArray &toCompare){
			bool isEqual = true;
			if(isEqual && this->theIndexList != toCompare.theIndexList ){
				isEqual = false;
			}
			return isEqual;
		}
		/*

		virtual bool operator==(const Type &toCompare ) {
			return this->equals(toCompare);
		}
		*/
		bool operator==(const TypeArray &toCompare ) {
			return this->equals(toCompare);
		}

		virtual bool operator!=(const TypeArray &toCompare ) {
			return ! this->equals(toCompare);
		}

		Type * clone() {
			TypeArray * t = new TypeArray(
					theTypeOfElem->clone(), 
					(TypeArrayIndexList *) theIndexList->clone());
			return t;
		}
};


class TypePointer: public Type {
	private:
		Type * typeOfPointer;

	public:

		TypePointer() {
			this->theType = TYPE_POINTER;
			this->typeOfPointer = NULL;
		}

		TypePointer(Type * typePointer) {
			this->theType = TYPE_POINTER;
			this->typeOfPointer = typePointer;
		}
		~TypePointer(){
			//delete this->typeOfPointer;
		}
		void show(){
			std::cout<< "^";
			typeOfPointer->show();
		}
		Type * clone() {
			Type * t = new TypePointer(typeOfPointer->clone());
			return t;
		}
		Type * getType(){
			return typeOfPointer;

		}
};

typedef std::vector<int> ListEnumValues;

class TypeEnum: public Type {
	private:
		ListEnumValues * listEnumVal;
		IdentTable * identTable;

	public:
		TypeEnum(ListEnumValues * listEnumValues, IdentTable * identTab) {
			this->theType = TYPE_ENUM;
			this->listEnumVal = listEnumValues;
			this->identTable = identTab;
		}
		~TypeEnum(){
			delete this->listEnumVal;
		}
		void show(){
			std::cout << "(";
			for(unsigned int i=0;i<listEnumVal->size();i++){
				std::cout << this->identTable->getName( (*listEnumVal)[i] );
				if( i != listEnumVal->size()-1 ){
					std::cout << ",";
				}
			}
			std::cout << ")";
		}

		void showElem(int position){
			std::cout << this->identTable->getName( (*listEnumVal)[position] );
		}
		Type * clone() {
			ListEnumValues * enumVals = new ListEnumValues(*this->listEnumVal);
			return new TypeEnum(enumVals, identTable);
		}
};



class TypeRecord : public Type {
	private:
		std::vector<int> * listRecordVal;
		SymbolTable * symTable;
		IdentTable * identTable;
		//ListRecordValues * listRecordVal;

	public:
		TypeRecord() {
			this->theType = TYPE_RECORD;
			this->symTable = NULL;
			this->listRecordVal = NULL;
			this->identTable = NULL;
		}
		TypeRecord(std::vector<int> *listIdent, SymbolTable * symTab, IdentTable * identTab) {
			this->theType = TYPE_RECORD;
			this->symTable = symTab;
			this->listRecordVal = listIdent;
			this->identTable = identTab;
		}
		~TypeRecord(){
			//if(this->listRecordVal!=NULL)
			//delete this->listRecordVal;
		}
		void show(){
			std::cout << "{";
			for(unsigned int i=0;i<listRecordVal->size();i++){
				int idIdent = (*listRecordVal)[i];

				std::cout << identTable->getName(idIdent) << ":";
				symTable->getById(idIdent)->getType()->show();

				if(i!=listRecordVal->size()-1)
					std::cout << ",";
			}
			std::cout << "}";
		}
		Type * clone() {
			return new TypeRecord(listRecordVal, symTable, identTable);
		}
};



#endif
